import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import org.apache.commons.io.DirectoryWalker;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Created by eahn on 01/11/13.
 */
public class DirectoryComparisonWalker extends DirectoryWalker<ComparisonResult> {

    private static Logger logger = LoggerFactory.getLogger(DirectoryComparisonWalker.class);

    protected File src;
    protected File dest;

    protected String srcPrefix;
    protected String destPrefix;

    protected List<ComparisonResult> result;

    public DirectoryComparisonWalker(File src, File dest) {
        super();
        this.src = src;
        this.dest = dest;

        this.srcPrefix = src.getAbsolutePath();
        this.destPrefix = dest.getAbsolutePath();

    }

    public List<ComparisonResult> start(Boolean bothDirection) throws IOException {

        // search for EQUAL, DIFFERENT and ONLY_IN_SRC
        result = search();

        if (bothDirection) {
            // search for ONLY_IN_DEST
            DirectoryComparisonWalker reverseSearcher = new DirectoryComparisonWalker(dest, src);
            List<ComparisonResult> reverseResult = reverseSearcher.start(Boolean.FALSE);
            for (ComparisonResult comparisonResult : reverseResult) {
                if (comparisonResult.getType() == ComparisonResult.ResultType.ONLY_IN_SRC) {
                    comparisonResult.setType(ComparisonResult.ResultType.ONLY_IN_DEST);
                    result.add(comparisonResult);
                }
            }
        }

        return result;
    }


    public Collection<ComparisonResult> getResult(final ComparisonResult.ResultType type) {

        return Collections2.filter(result, new Predicate<ComparisonResult>() {
            @Override
            public boolean apply(ComparisonResult comparisonResult) {

                return comparisonResult.getType() == type;
            }
        });

    }


    protected List<ComparisonResult> search() throws IOException {
        List<ComparisonResult> results = new ArrayList<ComparisonResult>();
        walk(src, results);
        return results;
    }


    protected boolean handleDirectory(File directory, int depth, Collection<ComparisonResult> results) {

        if (directory.equals(src)) {
            return true;
        }

        logger.debug("DIR: " + depth + " -> " + directory.getAbsolutePath());

        File toCompare = getFileToCompare(directory);

        if (toCompare.exists() && toCompare.isDirectory()) {

            results.add(new ComparisonResult(directory, ComparisonResult.ResultType.EQUAL, srcPrefix));

        } else {
            logger.debug("DIR not exists: '{}'", toCompare.getAbsolutePath());
            results.add(new ComparisonResult(directory, ComparisonResult.ResultType.ONLY_IN_SRC, srcPrefix));

        }

        return true;

    }

    protected void handleFile(File file, int depth, Collection<ComparisonResult> results) {

        logger.debug("FILE: " + depth + " -> " + file.getAbsolutePath());

        try {
            File toCompare = getFileToCompare(file);

            if (toCompare.exists() && toCompare.isFile()) {


                Boolean compareResult = FileUtils.contentEquals(file, toCompare);
                if (compareResult) {

                    results.add(new ComparisonResult(file, ComparisonResult.ResultType.EQUAL, srcPrefix));

                } else {

                    results.add(new ComparisonResult(file, ComparisonResult.ResultType.DIFFERENT, srcPrefix));
                }


            } else {

                logger.debug("FILE not exists: '{}'", toCompare.getAbsolutePath());
                results.add(new ComparisonResult(file, ComparisonResult.ResultType.ONLY_IN_SRC, srcPrefix));

            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    protected File getFileToCompare(File file) {

        String path = file.getAbsolutePath();

        path = path.replace(srcPrefix, "");
        path = destPrefix + path;

        File toCompare = new File(path);

        return toCompare;

    }

}
